package cn.yudao.ymcc.service.impl;

import cn.yudao.ymcc.dto.AggregationDto;
import cn.yudao.ymcc.dto.CourseEalsticSearchDto;
import cn.yudao.ymcc.dto.ElasticSearchListDto;
import cn.yudao.ymcc.esmapper.HighlightResultMapper;
import cn.yudao.ymcc.repository.CourseRepository;
import cn.yudao.ymcc.result.PageList;
import cn.yudao.ymcc.searchDoc.CourseDoc;
import cn.yudao.ymcc.service.IElasticSearchService;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ElasticSearchServiceImpl implements IElasticSearchService {

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;

    /**
    * @Title: save
    * @Description: 将数据新增到es中
    * @Author: Mr.Jia
    * @Version: 1.0
    * @Date:  2022/8/14 12:58
    * @Parameters:
    * @Return
    */
    @Override
    public void save(CourseDoc courseDoc) {
        // 将数据添加到es中
        courseRepository.save(courseDoc);
    }

    /**
    * @Title: delete
    * @Description: 将数据删除
    * @Author: Mr.Jia
    * @Version: 1.0
    * @Date:  2022/8/15 10:52
    * @Parameters:
    * @Return
    */
    @Override
    public void delete(Long id) {
        // 根据id删除
        courseRepository.deleteById(id);
    }


    /**
    * @Title: page
    * @Description: 从ES查询数据
    * @Author: Mr.Jia
    * @Version: 1.0
    * @Date:  2022/8/16 16:35
    * @Parameters:
    * @Return
    */
    @Override
    public PageList<CourseDoc> page(CourseEalsticSearchDto dto) {
        //
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 添加分页条件=========
        PageRequest of = PageRequest.of(dto.getPage()-1, dto.getRows());
        nativeSearchQueryBuilder.withPageable(of);
        // 添加排序条件=========
        SortOrder order = SortOrder.ASC;
        // -- 因为前端传回来的数据有问题，所以我们需要自己判断
        String sortField = "";
        if (StringUtils.isNotBlank(dto.getSortField())){
            switch (dto.getSortField()){
                case "xl" : sortField = "saleCount"; break;
                case "xp" : sortField = "onlineTime"; break;
                case "pl" : sortField = "commentCount"; break;
                case "jg" : sortField = "price"; break;
                case "rq" : sortField = "viewCount"; break;
            }
        }
        if (StringUtils.isNotBlank(dto.getSortType())){
            order = dto.getSortType().equals("asc")?SortOrder.ASC:SortOrder.DESC;
        }
        if (StringUtils.isNotBlank(sortField)){
            FieldSortBuilder sortBuilder = SortBuilders.fieldSort(sortField).order(order);
            nativeSearchQueryBuilder.withSort(sortBuilder);
        }
        // 参数查询判断
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 获取boolquery

        // 判断keyword是否为空，使用must模糊分词match查询
        if (StringUtils.isNotBlank(dto.getKeyword())){
            MatchQueryBuilder name = QueryBuilders.matchQuery("name", dto.getKeyword());// 使用must模糊分词match查询
            boolQuery.must(name);// 获取mustquery
        }
        // 判读--使用过滤查询
//        private Long courseTypeId; // 课程分类
//        private String gradeName; //等级名称
//        private Double priceMin; // 最小价格
//        private Double priceMax; // 最大价格
        if (dto.getCourseTypeId() != null){
            TermQueryBuilder courseTypeId = QueryBuilders.termQuery("courseTypeId", dto.getCourseTypeId());
            boolQuery.filter(courseTypeId);
        }
        if (StringUtils.isNotBlank(dto.getGradeName())){
            TermQueryBuilder gradeName = QueryBuilders.termQuery("gradeName", dto.getGradeName());
            boolQuery.filter(gradeName);
        }
        if (dto.getPriceMax() != null){
            RangeQueryBuilder priceMax = QueryBuilders.rangeQuery("price").lte(dto.getPriceMax());
            boolQuery.filter(priceMax);
        }
        if (dto.getPriceMin() != null){
            RangeQueryBuilder priceMin = QueryBuilders.rangeQuery("price").gte(dto.getPriceMin());
            boolQuery.filter(priceMin);
        }
        nativeSearchQueryBuilder.withQuery(boolQuery);

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        // 设置高亮
        nativeSearchQueryBuilder.withHighlightFields(
                new HighlightBuilder.
                        Field("name")
                        .preTags("<font style = 'color : red'>")
                        .postTags("</font>"));

        // 聚合查询 -- 分组和每组的数量--添加聚合规则
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("gradeNameAggs").field("gradeName"));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("chargeNameAggs").field("chargeName"));

        // 条件查询完后的返回
        NativeSearchQuery build = nativeSearchQueryBuilder.build();
        AggregatedPage<CourseDoc> search = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);

        // 对聚合查询的数据进行封装
        Aggregations aggregations = search.getAggregations(); // 拿到所有的聚合数据
        Map<String, Aggregation> map = aggregations.asMap(); // 将数据转换为map
        ParsedStringTerms gradeNameAggs = (ParsedStringTerms)map.get("gradeNameAggs");
        ParsedStringTerms chargeNameAggs = (ParsedStringTerms)map.get("chargeNameAggs");
        List<? extends Terms.Bucket> gradeNameAggBuckets = gradeNameAggs.getBuckets();
        List<? extends Terms.Bucket> chargeNameAggBuckets = chargeNameAggs.getBuckets();
        // 将集合转换为我们需要的格式
        List<AggregationDto> gradeNameCollect = gradeNameAggBuckets.stream()
                .map(bucket -> new AggregationDto(bucket.getKeyAsString(), bucket.getDocCount()))
                .collect(Collectors.toList());
        List<AggregationDto> chargeNameCollect = chargeNameAggBuckets.stream()
                .map(bucket -> new AggregationDto(bucket.getKeyAsString(), bucket.getDocCount()))
                .collect(Collectors.toList());
        // 将我们的数据装到我们的map集合中
        Map<String, List<AggregationDto>> returnMap = new HashMap<>();
        returnMap.put("gradeNameAggs",gradeNameCollect);
        returnMap.put("chargeNameAggs",chargeNameCollect);
        //Page<CourseDoc> search = courseRepository.search(build);
        return new ElasticSearchListDto(search.getTotalElements(),search.getContent(),returnMap);
    }



    /**
    * @Title: page
    * @Description: 根据搜索条件查询ES -- 无高亮版本
    * @Author: Mr.Jia
    * @Version: 1.0
    * @Date:  2022/8/15 12:49
    * @Parameters:
    * @Return
    */
//    @Override
    public PageList<CourseDoc> pageNotHigtLighted(CourseEalsticSearchDto dto) {
        //
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 添加分页条件=========
        PageRequest of = PageRequest.of(dto.getPage()-1, dto.getRows());
        nativeSearchQueryBuilder.withPageable(of);
        // 添加排序条件=========
        SortOrder order = SortOrder.ASC;
        // -- 因为前端传回来的数据有问题，所以我们需要自己判断
        String sortField = "";
        if (StringUtils.isNotBlank(dto.getSortField())){
            switch (dto.getSortField()){
                case "xl" : sortField = "saleCount"; break;
                case "xp" : sortField = "onlineTime"; break;
                case "pl" : sortField = "commentCount"; break;
                case "jg" : sortField = "price"; break;
                case "rq" : sortField = "viewCount"; break;
            }
        }
        if (StringUtils.isNotBlank(dto.getSortType())){
            order = dto.getSortType().equals("asc")?SortOrder.ASC:SortOrder.DESC;
        }
        if (StringUtils.isNotBlank(sortField)){
            FieldSortBuilder sortBuilder = SortBuilders.fieldSort(sortField).order(order);
            nativeSearchQueryBuilder.withSort(sortBuilder);
        }
        // 参数查询判断
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 获取boolquery

        // 判断keyword是否为空，使用must模糊分词match查询
        if (StringUtils.isNotBlank(dto.getKeyword())){
            MatchQueryBuilder name = QueryBuilders.matchQuery("name", dto.getKeyword());// 使用must模糊分词match查询
            boolQuery.must(name);// 获取mustquery
        }
        // 判读--使用过滤查询
//        private Long courseTypeId; // 课程分类
//        private String gradeName; //等级名称
//        private Double priceMin; // 最小价格
//        private Double priceMax; // 最大价格
        if (dto.getCourseTypeId() != null){
            TermQueryBuilder courseTypeId = QueryBuilders.termQuery("courseTypeId", dto.getCourseTypeId());
            boolQuery.filter(courseTypeId);
        }
        if (StringUtils.isNotBlank(dto.getGradeName())){
            TermQueryBuilder gradeName = QueryBuilders.termQuery("gradeName", dto.getGradeName());
            boolQuery.filter(gradeName);
        }
        if (dto.getPriceMax() != null){
            RangeQueryBuilder priceMax = QueryBuilders.rangeQuery("price").lte(dto.getPriceMax());
            boolQuery.filter(priceMax);
        }
        if (dto.getPriceMin() != null){
            RangeQueryBuilder priceMin = QueryBuilders.rangeQuery("price").gte(dto.getPriceMin());
            boolQuery.filter(priceMin);
        }
        nativeSearchQueryBuilder.withQuery(boolQuery);
        // 条件查询完后的返回
        NativeSearchQuery build = nativeSearchQueryBuilder.build();
        Page<CourseDoc> search = courseRepository.search(build);
        return new PageList<>(search.getTotalElements(),search.getContent());
    }
}
